﻿using CommandSystem;
using Exiled.API.Enums;
using Exiled.API.Features;
using Exiled.API.Features.Pools;
using RemoteAdmin;
using RgCore.Commands;
using RgCore.Menu;
using RgCore.Modules;
using RgCore.PlayerHud;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;

namespace VotingSystem
{
    public class VoteCommand : ICommand
    {
        public string Command => "votesys";

        public string[] Aliases => Array.Empty<string>();

        public string Description => "投票系统命令";

        public bool Execute(ArraySegment<string> arguments, ICommandSender sender, out string response)
        {
            response = "";
            if (arguments.Count == 0)
                return false;
            if (arguments.ElementAt(0) == "shut")
            {
                if (VotingHandler.CurrentVoting == null)
                {
                    response = "当前没有正在进行的投票";
                    return false;
                }
                VotingHandler.EndVote(true);
                response = "已强制废除投票";
                return true;
            }
            if (arguments.ElementAt(0) == "refreshcd")
            {
                VotingHandler.LastVoteTime = DateTime.MinValue;
            }
            if (arguments.ElementAt(0) == "reload")
            {
                if (VotingHandler.CurrentVoting != null)
                {
                    response = "当前有正在进行的投票，请使用votesys shut来废除投票";
                    return false;
                }
                foreach (Player player in Player.List)
                {
                    player.SendFormattedMsg("hotfix", "<color=#FF2222>热更新</color>", "投票系统正在重新加载所有投票项", 3, MsgType.Announcement, ScreenLocation.CenterTop, true);
                }
                VotingHandler.AllVotables.Clear();
                MenuInterface.RegisteredElements.RemoveAll(x => VotingPlugin.Registered.Contains(x));
                VotingPlugin.Registered.Clear();
                VotingPlugin.InitTypes();
                VotingPlugin.PatchMenu(false);
                response = "已重新加载投票系统的所有投票项";
                return true;
            }
            return false;
        }
    }

    public class VotingPlugin : RgPlugin
    {
        public override string Name => "投票系统";

        public override string Id => "VotingSystem";
        public override PluginPriority Priority => PluginPriority.Low;
        public override bool CanUnload => false;
        public static string VotesPath = Path.Combine(RgCore.Modules.ModuleManager.SubPluginPath, "Votes");
        public static List<MenuElement> Registered = new List<MenuElement>();
        public static ICommand cmd = new VoteCommand();

        public override void OnEnabled()
        {
            InitTypes();
            PatchMenu(true);
            DecisionCommand.PlayerAgree += PlayerAgree;
            DecisionCommand.PlayerRefuse += PlayerRefuse;
            Exiled.Events.Handlers.Server.RoundEnded += Server_RoundEnded;
            Exiled.Events.Handlers.Server.RestartingRound += Server_RestartingRound;
            CommandProcessor.RemoteAdminCommandHandler.RegisterCommand(cmd);
            VotingHandler.Thread = new Thread(VotingHandler.Update) { Name = "投票系统" };
            VotingHandler.Thread.Start();
        }

        private void Server_RoundEnded(Exiled.Events.EventArgs.Server.RoundEndedEventArgs ev)
        {
            if (VotingHandler.CurrentVoting != null)
            {
                foreach (Player player in Player.List)
                {
                    player.DeleteMsg("vote", false);
                    player.SendFormattedMsg("vote_result", "<color=#FF6A6A>投票被废除</color>", "回合已结束", 5, MsgType.Announcement, ScreenLocation.CenterTop, true);
                }
                VotingHandler.CurrentVoting = null;
                VotingHandler.LastVoteTime = DateTime.Now;
            }
        }
        
        private void Server_RestartingRound()
        {
            if (VotingHandler.CurrentVoting != null)
            {
                VotingHandler.CurrentVoting = null;
                VotingHandler.LastVoteTime = DateTime.Now;
            }
        }

        public override void OnDisabled()
        {
            DecisionCommand.PlayerAgree -= PlayerAgree;
            DecisionCommand.PlayerRefuse -= PlayerRefuse;
            Exiled.Events.Handlers.Server.RoundEnded -= Server_RoundEnded;
            CommandProcessor.RemoteAdminCommandHandler.UnregisterCommand(cmd);
            VotingHandler.AllVotables.Clear();
            MenuInterface.RegisteredElements.Remove(VotingHandler.PageRoot);
            MenuInterface.RegisteredElements.RemoveAll(x => Registered.Contains(x));
            VotingHandler.Thread.Abort();
        }

        private void PlayerRefuse(Player player)
        {
            if (VotingHandler.CurrentVoting != null && !VotingHandler.Voted.Contains(player.UserId))
            {
                VotingHandler.Voted.Add(player.UserId);
                VotingHandler.Refuse++;
                player.SendRawMsg("", "[你选择了<color=#FF0000>反对</color>票]", 2, ScreenLocation.CenterTop, true);
            }
        }

        private void PlayerAgree(Player player)
        {
            if (VotingHandler.CurrentVoting != null && !VotingHandler.Voted.Contains(player.UserId))
            {
                VotingHandler.Voted.Add(player.UserId);
                VotingHandler.Agree++;
                player.SendRawMsg("", "[你选择了<color=#00FF00>同意</color>票]", 2, ScreenLocation.CenterTop, true);
            }
        }

        public static void InitTypes()
        {
            Directory.CreateDirectory(VotesPath);
            foreach (string text in Directory.GetFiles(VotesPath, "*.dll"))
            {
                Assembly assembly = Assembly.Load(File.ReadAllBytes(text));
                if (assembly != null)
                {
                    foreach (Type t in assembly.GetTypes())
                    {
                        if (typeof(Votable).IsAssignableFrom(t))
                        {
                            Votable v = (Votable)Activator.CreateInstance(t);
                            VotingHandler.AllVotables.Add(v);
                            Log.Info("【投票系统】加载投票项：" + t.Name + $" ({v.Attribute.Name})");
                        }
                    }
                }
            }
        }

        public static void PatchMenu(bool init)
        {
            if (init)
            {
                Page root = new Page(MenuInterface.Root, "Voting", "发起投票", null, CanLaunchVote, null, false);
                VotingHandler.PageRoot = root;
                MenuInterface.RegisteredElements.Add(root);
            }
            VotingHandler.PageRoot.Children = (px) =>
            {
                var list = ListPool<MenuElement>.Pool.Get();
                foreach (var type in VotingHandler.AllVotables)
                {
                    VoteAttribute va = type.Attribute;
                    if (type.GetSelections().Any())
                    {
                        Page pg = new Page(VotingHandler.PageRoot, type.GetType().Name, va.Name, va.Display, (Player p, out string r) =>
                            type.Launchable(p, out r, null), null, false);
                        pg.Children = (player) =>
                        {
                            var menu = ListPool<MenuElement>.Pool.Get();
                            foreach (var selection in type.GetSelections())
                            {
                                Option option = new Option(pg, $"{type.GetType().Name}_{selection.Text}", selection.Text, (_) => selection.Text,
                                    (Player p, out string r) => type.Launchable(p, out r, selection), (Player p) => type.TryLaunch(player, selection), false);
                                menu.Add(option);
                            }
                            var array = menu.ToArray();
                            ListPool<MenuElement>.Pool.Return(menu);
                            return array;
                        };
                        list.Add(pg);
                    }
                    else
                    {
                        Option instance = new Option(VotingHandler.PageRoot, type.GetType().Name, va.Name, va.Display,
        (Player p, out string r) => type.Launchable(p, out r, null), (Player p) => type.TryLaunch(p, null), false);
                        list.Add(instance);
                    }
                }
                var ar = list.ToArray();
                ListPool<MenuElement>.Pool.Return(list);
                return ar;
            };
        }

        public static bool CanLaunchVote(Player player, out string response)
        {
            response = "";
            if (!Round.IsStarted)
            {
                response = "开局前不可发起投票";
                return false;
            }
            if ((DateTime.Now - VotingHandler.LastVoteTime).TotalMinutes < 2)
            {
                response = "2分钟内不可再次发起投票";
                return false;
            }
            if (VotingHandler.CurrentVoting != null)
            {
                response = "当前已经有一个正在进行的投票";
                return false;
            }
            return true;
        }
    }

    public static class VotingHandler
    {
        public static Thread Thread;
        public static List<Votable> AllVotables { get; set; } = new List<Votable>();
        public static Votable CurrentVoting { get; set; } = null;
        public static Page PageRoot { get; set; }
        public static int Agree { get; set; } = 0;
        public static int Refuse { get; set; } = 0;
        public static List<string> Voted { get; set; } = new List<string>();
        public static DateTime LastVoteTime { get; set; } = DateTime.Now;

        public static void Update()
        {
            while (true)
            {
                try
                {
                    if (CurrentVoting != null)
                    {
                        var elapsed = (DateTime.Now - CurrentVoting.LaunchTime).TotalSeconds;
                        if (elapsed >= 60
                            || ((Agree / Player.List.Count()) >= CurrentVoting.Attribute.PassPercent && elapsed > 20))
                        {
                            EndVote(false);
                        }
                    }
                }
                catch (Exception e)
                {
                    Log.Error(e);
                }
                Thread.Sleep(1000);
            }
        }

        public static string[] GetString(Player player)
        {
            if (CurrentVoting == null)
                return new string[] { "<color=#FFAAAA>没有进行的投票</color>" };
            int total = Player.List.Count();
            int quit = total - Agree - Refuse;
            double percent = (Agree + quit * 0.2) / total;
            string p = $"{(int)(percent * 100)}%";
            int remain = (CurrentVoting.LaunchTime.AddSeconds(60) - DateTime.Now).Seconds;
            string r = $"<color=#FF7F24>{CurrentVoting.LauncherName}</color>发起了投票：{CurrentVoting.BroadcastTitle()} " +
                $"(<color=#00FF00>{Agree}</color> | <color=#FF0000>{Refuse}</color> | <color=yellow>{p}</color>) [{remain}]";
            List<string> result = new List<string>
            {
                r
            };
            if (!Voted.Contains(player.UserId))
            {
                result.Add($"<color=#00CED1>.v ty</color> (<color=#00CED1>F9</color>) 同意 | <color=#00CED1>.v fd</color> (<color=#00CED1>F10</color>) 反对");
            }
            return result.ToArray();
        }

        public static void LaunchVote(Votable v, Selection? selection)
        {
            if (CurrentVoting != null)
                return;
            Agree = 0;
            Refuse = 0;
            Voted.Clear();
            v.LaunchTime = DateTime.Now;
            CurrentVoting = v;
            CurrentVoting.Selection = selection;
            HudHandler.SendRawMsg("vote", GetString, 60, ScreenLocation.Top, true, Player.List.ToArray());
        }

        public static void EndVote(bool shutdowned)
        {
            if (CurrentVoting == null)
                return;
            foreach (Player player in Player.List)
            {
                player.DeleteMsg("vote", false);
            }
            if (shutdowned)
            {
                foreach (Player player in Player.List)
                {
                    player.SendFormattedMsg("vote_result", "<color=#FF6A6A>投票被废除</color>", "管理员强制废除了该投票", 5, MsgType.Announcement, ScreenLocation.CenterTop, true);
                }
            }
            else
            {
                int total = Player.List.Count();
                int quit = total - Agree - Refuse;
                double percent = (Agree + quit * 0.2) / total;
                bool passed = percent >= CurrentVoting.Attribute.PassPercent;
                string title = passed ? "<color=#7FFF00>投票通过</color>" : "<color=#FF6A6A>投票不通过</color>";
                string content = passed ? "" : "投票同意率未达到标准";
                if (passed)
                {
                    string result = CurrentVoting.OnPassed();
                    content = result;
                }
                foreach (Player player in Player.List)
                {
                    player.SendFormattedMsg("vote_result", title, content, 5, MsgType.Announcement, ScreenLocation.CenterTop, true);
                }
            }
            CurrentVoting = null;
            LastVoteTime = DateTime.Now;
        }
    }

    public struct Selection
    {
        public string Text;
        public object[] Parameters;
    }

    public abstract class Votable
    {
        public string LauncherUserId { get; set; }
        public string LauncherName { get; set; }
        public DateTime LaunchTime { get; set; }
        public VoteAttribute Attribute => this.GetType().GetCustomAttribute<VoteAttribute>();

        public abstract string OnPassed();

        public Selection? Selection { get; set; }

        public virtual IEnumerable<Selection> GetSelections() => Array.Empty<Selection>();

        public virtual string BroadcastTitle()
        {
            return $"<color=#00FFFF>{Attribute.Name}</color>";
        }

        public virtual bool Launchable(Player player, out string response, Selection? selection)
        {
            response = "";
            return true;
        }

        public virtual string TryLaunch(Player player, Selection? selection)
        {
            bool result = Launchable(player, out string response, selection);
            if (!result)
                return response;
            LauncherUserId = player?.UserId;
            LauncherName = player?.Nickname;
            VotingHandler.LaunchVote(this, selection);
            return "";
        }
    }

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public class VoteAttribute : Attribute
    {
        public VoteAttribute(string name, double percent)
        {
            Name = name;
            PassPercent = percent;
        }

        public string Name { get; }
        public double PassPercent { get; }

        public string Display(Player _)
        {
            return $"{Name} [<color=#00FF00>{(int)(PassPercent * 100)}%</color>]";
        }
    }
}